package de.tu_darmstadt.gdi1.battleship.model;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Random;

import de.tu_darmstadt.gdi1.battleship.exceptions.InvalidCoordinatesException;

public final class CPUnew {

	private Coordinates[] posTargets;

	private int count = 0;
	private int rounds = -1;
	private int difficulty = 2;

	private SingleMap humap,cmap;
	private Coordinates nextTarget;

	private int notify = 0;

	private boolean seekup = false;
	private Coordinates saveFirst, saveLast;

	public CPUnew() {
		
		humap = new SingleMap();
		nextTarget = randomHit();		// Hier wird posTargets initialisiert.
		try {saveFirst = saveLast = new Coordinates(0, 0);} catch (InvalidCoordinatesException e) {}
	}
	
	public CPUnew(SingleMap huMap, SingleMap cpuMap){
		this.humap = huMap;
		this.cmap = cpuMap;
		
		nextTarget = randomHit();		// Hier wird posTargets initialisiert.
		try {saveFirst = saveLast = new Coordinates(0, 0);} catch (InvalidCoordinatesException e) {}
	}
	
	
	
	/**
	 * chooses a random coordinate of all possibilities
	 * 
	 * 
	 * @param all
	 *            possible Coordinates
	 */
	public Coordinates CPUdecision(Coordinates[] coords) {
		Random ran = new Random();
		int position = ran.nextInt(coords.length);
		return coords[position];
	}
	
	public String generateSaveString() {

		StringBuffer sb = new StringBuffer("#CPU\n");

		// posTarget
		sb.append(posTargets.length).append('\n');
		for (int i = 0; i < posTargets.length; i++) {
			sb.append(posTargets[i].generateSaveString());
		}

		// int werte
		sb.append(count).append("\n");
		sb.append(rounds).append("\n");
		sb.append(notify).append("\n");

		// map
		sb.append(humap.generateSaveString());

		// bool werte
		sb.append(seekup).append("\n");

		// coordinaten einfach
		sb.append(saveFirst.generateSaveString());
		sb.append(saveLast.generateSaveString());
		sb.append(nextTarget.generateSaveString());

		return sb.toString();

	}

	/**
	 * gets an array of the unlocked neighbour's Coordinates
	 * 
	 * @param acoord
	 * coordinate of which the neighbours are calculated
	 * 
	 * @return
	 */
	public Coordinates[] getNeighbourCoords(Coordinates acoord) {
		int x = acoord.getX();
		int y = acoord.getY();

		int startx = (x != 0) ? x - 1 : x;
		int starty = (y != 0) ? y - 1 : y;
		int endx = (x == 9) ? x : x + 1;
		int endy = (y == 9) ? y : y + 1;

		humap.lockTemp();
		humap.getEntry(x, starty).setTempLock(false);
		humap.getEntry(x, endy).setTempLock(false);
		humap.getEntry(startx, y).setTempLock(false);
		humap.getEntry(endx, y).setTempLock(false);

		return humap.getUnlockedFields();

	}

/**
 * calculates the next target for the cpu to shoot
 * @param hu
 * human map
 * @param cpu
 * cpu map
 * @return
 * @throws InvalidCoordinatesException
 */
	public Coordinates getNewTarget(SingleMap hu, SingleMap cpu)
			throws InvalidCoordinatesException {
		humap = hu;
		cmap=cpu;
		if (difficulty == 0) {
			nextTarget = randomHit();
		} else {

			if (notify == 0) {
				nextTarget = randomHit();
			}

			if (notify == 1) {
				nextTarget = CPUdecision(getNeighbourCoords(saveFirst));
				humap.unlockTemp();

			}

			if (notify >= 2) { // falls 2 nebeneinanderliegende treffer gemacht
								// wurden
				secondhit();

			}
		}
		humap.getEntry(nextTarget).setPermLock(true);
		return nextTarget;
	}
/**
 * reads a cpu save string
 * @param br
 * BufferedReader
 * @throws IOException
 */
	public void loadSaveString(BufferedReader br) throws IOException {

		if (br.readLine().equals("#CPU")) {

			// posTarget
			int a = Integer.valueOf(br.readLine());
			for (int i = 0; i < a; i++) {
				Coordinates c = null;
				try {
					c = new Coordinates(0, 0);
				} catch (InvalidCoordinatesException e) {
				}
				c.loadSaveString(br);
				posTargets[i] = c;
			}

			// int
			count = Integer.valueOf(br.readLine());
			rounds = Integer.valueOf(br.readLine());
			notify = Integer.valueOf(br.readLine());

			// map
			humap.loadSaveString(br);

			// bool
			seekup = Boolean.valueOf(br.readLine());

			// coords
			saveFirst.loadSaveString(br);
			saveLast.loadSaveString(br);
			nextTarget.loadSaveString(br);

		} else {
			System.err.println("Buffered Reader does not begin with #CPU");
		}
	}
/**
 * method which notifies if a ship part has been hit 
 */
	public void notifyhit() {
		notify++;
		if (notify == 1) { // falls er getroffen hat speichert er die koords
			saveFirst = nextTarget;
		}
		if (notify >= 2) {// beim zweiten treffer speichert er wieder die
							// koords
			saveLast = nextTarget;
		} 
	}
/**
 * method which notifies if a whole ship has been hit and is down
 * @param ship
 */
	public void notifyShipDown(Ship ship) {
			notify = 0;
			humap.pLockFields(ship.getStart().getX(), ship.getStart().getY(),
					ship.getLength(), ship.getVertical());
	}
/**
 * checks if the ship is horizontal or vertical oriented
 * @param a
 * coordinate of first ship hit
 * @param b
 * coordinate of second ship hit
 * @return
 * true if vertical
 */
	public boolean orientationTester(Coordinates a, Coordinates b) {
		if (a.getX() == b.getX()) // schiff vertikal
			return true;
		else
			return false; // schiff horizontal

	}

	/**
	 * selects random a field , if the cpu is advanced it uses only every second field
	 * 
	 */
	private Coordinates randomHit() {

		if (difficulty == 2) {
			for (int i = 0; i < 10; i++) {
				for (int j = 0; j < 10; j++) {
					if ((i + j) % 2 == 0) {
						humap.getEntry(i, j).setTempLock(true);
					} else {
						humap.getEntry(i, j).setTempLock(false);
					}
				}

			}
		}

		posTargets = humap.getUnlockedFields();
		humap.unlockTemp();
		return CPUdecision(posTargets);
	}
/**
 * resets the hit - information stored in the cpu
 */
	public void resetCPU() {
		notify = 0;
	}

	
/**
 * method for shooting right after two hits could be landed on a ship
 * @throws InvalidCoordinatesException
 */
	public void secondhit()
			throws InvalidCoordinatesException {
		char[][] shadow = humap.getEnemyView();
		seekup = true;
		int m = saveFirst.getY();
		int n = saveFirst.getX();
		while (seekup == true) {
			if (orientationTester(saveFirst, saveLast)) // jenachdem wies schiff liegt
													// wird die passende
													// koordinate nach oben
													// gezaehlt
				m++;
			else
				n++;
			if (n <= 9 && n >= 0 && m <= 9 && m >= 0) {

				// sucht nach oben, also richtung bottom/right
				
				if (shadow[n][m] == 'f') {
					
					try {nextTarget = new Coordinates(n, m);
						 return;
						} 
					catch (InvalidCoordinatesException ice) {}//sollte nicht passieren

				}
				if (shadow[n][m] == '*') {// falls da schon beschossenes Wasser
											// ist, probiert er die andere Richtung
					seekup = false;
				}
			}
			else {
				seekup = false;
			}
		}

		while (seekup == false) {
			// wenns oben zuende is zaehlt er nach unten weiter

			if (orientationTester(saveFirst, saveLast))// jenachdem wies schiff liegt
												// wird die richtige koordinate
												// nach unten gezaehlt
				m--;
			else
				n--;
			
			if (n <= 9 && n >= 0 && m <= 9 && m >= 0) {
				
				if (shadow[n][m] == 'f') {
					try {nextTarget = new Coordinates(n, m);
					  	 return;
						} 
					catch (InvalidCoordinatesException ice) {
						System.err.println("Eclipse 'if' failed.");
					}

				}
				if (shadow[n][m] == '*') {
					seekup = true;
				}
			}
			else {
				seekup = true;
			}

		}
	} 

	
	
	/**
	 * in ammunition mode cpu chooses his ship with ammo randomly
	 */
	public void CPUchooseShipToShoot() {
		cmap.lockTemp();

		for (int i = 0; i < 10; i++) { // x
			for (int j = 0; j < 10; j++) { // y
				if ((cmap.getMap()[i][j].getSymbol() == 'v'||
						cmap.getMap()[i][j].getSymbol() == 't'||
						cmap.getMap()[i][j].getSymbol() == 'h'||
						cmap.getMap()[i][j].getSymbol() == 'b'||
						cmap.getMap()[i][j].getSymbol() == 'l'||
						cmap.getMap()[i][j].getSymbol() == 'r')
						&& cmap.getMap()[i][j].getAmmunition() > 0)  //unlockt alle schiffsteile die noch munition haben
					
				{cmap.getEntry(i, j).setTempLock(false);}

			}
		}

		Random ran = new Random();
		int position = ran.nextInt(cmap.getUnlockedFields().length); //w�hlt random eins aus
		Coordinates shootingShip= cmap.getUnlockedFields()[position]; 
		
		cmap.getMap()[shootingShip.getX()][shootingShip.getY()] // un verringert Munition um eins 
		         						.setAmmunition(cmap.getMap()[shootingShip.getX()][shootingShip
		         								.getY()].getAmmunition() - 1);
	}

	/**
	 * the cpu recognizes if a new level has been started
	 */
	public void notifyNewLevel(){
		char[][] enV = humap.getEnemyView();
		humap.lockPerm();
		for (int x = 0; x < 10; x++) { 
			for (int y = 0; y < 10; y++) {	
				if (enV[x][y] == 'f'){
					humap.getEntry(x, y).setPermLock(false);
				}
			}
		}
	}
}
